import axios from 'axios'
import fisco from '../lib/fisco-bcos/lib'
import { ethers } from "ethers";
import { v4 as uuidv4 } from 'uuid';

//封装axios,拦截返回
axios.interceptors.response.use(
  response => {
    if(typeof response.data.error !== "undefined"){
      console.log('axios接口请求失败',response.data.error)
    } else {
      return response;
    }
  },
  error => {
    console.log('axios接口请求失败',error)
  }
);



//所有的参数都应该收敛到方法内，让调用的时候都是一个独立的作用域
//定义runweb3方法接收两个参数 1.要执行的参数,包括请求方法名，方法需要传的值 2.要执行的账户，包括公钥和私钥 3、合约内容 4、可选参数optional，包括每次调用回调的间隔时间
function reqweb3(param1,myaccount,contarct,optional){
  const env = process.env.NODE_ENV
  var baseUrl = 'http://172.30.5.22:8655'
  if(env==='development'){
    // baseUrl = '/jsonRpc'
    baseUrl = "http://172.30.5.22:8655"
  }else if(env==='production'){
    baseUrl = "http://172.30.5.22:8655"
  }
  //初始化参数
  let jsonRpcEndPoint = baseUrl;
  let chainID = 1;
  let groupID = 1;
  let contractName = "Witness";
  let encryptType = fisco.ENCRYPT_TYPE.ECDSA;
  // 合约函数的decoder
  let decoder;

  let inputStr = param1
  const argv = inputStr.split(" ");
  let functionName = argv[0];
  let parameters = argv.slice(1);
  fisco.typeCheck.check([myaccount.address, contractName, contarct.addr, functionName, parameters],
    fisco.typeCheck.Addr, fisco.typeCheck.Str, fisco.typeCheck.Addr, fisco.typeCheck.Str, fisco.typeCheck.ArrayList);

  let inputsReg = /\(.*\)/;
  let inputs = inputsReg.exec(functionName);
  if (inputs) {
    inputs = inputs[0];
    inputs = inputs.substring(1, inputs.length - 1).split(',');
    inputs = inputs.map((input) => input.trim());
  }

  // 获取abi对象
  let pureFunctionName = functionName.replace(inputsReg, '');
  let abi = getAbi(pureFunctionName, inputs, contarct.abi);
  fisco.typeCheck.check([abi],fisco.typeCheck.Obj);
  // 合约函数的decoder
  decoder = fisco.decoder.createMethodDecoder(abi, null);
  if (abi.stateMutability === "view" || abi.stateMutability === "pure") {           //只读
    let iface = new ethers.utils.Interface(contarct.abi);        //连接合约
    let abi = iface.functions[pureFunctionName];
    _checkParameters(abi, parameters);      //检查参数
    // 打包交易data
    let txData = _getTxData(abi, parameters, encryptType);
    return new  Promise((resolve,reject)=>{
      axios.post(jsonRpcEndPoint, {
        "jsonrpc": "2.0",
        "method": "call",
        "params": [
          groupID,
          {
            "from": myaccount.address,
            "to": contarct.addr,      //合约请求地址
            "value": "0x0",
            "data": txData
          }
        ],
        "id": 1
      }).then(res => {
        let status = res.data.result.status;
        let ret = {
          status: status
        };
        let output = res.data.result.output;
        if (output !== '0x') {
          // console.log(Buffer.from(output, 'hex').length)
          ret.output = decoder.decodeOutput(output);
        }
        resolve(ret)
      });
    })
  }else{
    /*
    需要上链打包的方法，用sendRawTransaction调用
    */
    let params = parameters;
    return axios.post(contarct.req, {    // 需要先查当前区块高度
      "jsonrpc": "2.0",
      "method": "getBlockNumber",
      "params": [
          groupID,
      ],
      "id": 1
    }).then(res => {
      // 当前区块高度
      let blockNumber = parseInt(res.data.result);
      // limit
      let blockLimit = parseInt(res.data.result) + 500;
      // 打包交易data
      let txData = _getTxData(abi, params, encryptType);
      // 组装postData
      let postData = {
        data: txData,
        from: myaccount.address,
        to: contarct.addr,      //发送到合约地址
        gas: 1000000,           //花费
        randomid: _genRandomID(),
        blockLimit: blockLimit,         //块高
        chainId: chainID,
        groupId: groupID,
        extraData: '0x0',
      };
      // console.log('组装数据都完成了',postData)
      // 交易签名
      try {
        var signedTx = fisco.web3sync.signTransaction(postData, myaccount.privateKey, encryptType, null);
      } catch (error) {
        console.log(error)
      }
      // return
      console.log('准备执行交易上链');
      // 交易上链sendRawTransaction
      return axios.post(contarct.req, {
        "jsonrpc": "2.0",
        "method": "sendRawTransaction",
        "params": [
            groupID,
            signedTx
        ],
        "id": 1
      });
    }).then(res => {
      //交易完成
      // 交易hash
      let txHash = res.data.result;
      // 通过hash获取交易回执。可放入具体代码中执行
      console.log('交易完成，得到的hash',txHash)
      console.log('准备执行获取回执');
      let param = {
        jsonRpcEndPoint:jsonRpcEndPoint,
        groupID:groupID,
        decoder:decoder
      }
      return showTransactionReceipt(txHash,param,optional);
    });
  }
}
//休眠
function sleep(duration){
  return new Promise(function(resolve){
      setTimeout(resolve, duration);
  })
}
//获取回执。成功才返回
async function showTransactionReceipt(txHash,param,optional){
  return axios.post(param.jsonRpcEndPoint, {
    "jsonrpc": "2.0",
    "method": "getTransactionReceipt",
    "params": [
      param.groupID,
      txHash
    ],
    "id": 1
  }).then(res => {
    let result = res.data.result;
    if(result === null){
      console.log('回执为空，我还要再等一秒')
      let time = optional.reqReceiptTime?optional.reqReceiptTime:1000
      return new Promise(resolve =>
        setTimeout(() => {
          resolve(showTransactionReceipt(txHash,param,optional))
        },time)
      );
    }else{
      let ret = {
        transactionHash: result.transactionHash,
        status: result.status
      };
      let output = result.output;
      if (output !== '0x') {
        ret.output = param.decoder.decodeOutput(output);
      }
      // console.log('回执不为空，得到的内容是',JSON.stringify(ret, null, "    "))
      return new Promise((resolve,reject)=>{
        resolve(JSON.stringify(ret, null, "    "))
      })
    }
  });
}

function getAbi(functionName, inputs,Abi) {
  let contractAbi = JSON.parse(Abi)
  if (functionName) {
    if (inputs && inputs.length > 0) {
      return contractAbi.find((item) => {
          return item.type === 'function' && item.name === functionName && compareInputs(item.inputs, inputs);
      });
    } else {
      return contractAbi.find((item) => {
        return item.type === 'function' && item.name === functionName;
      });
    }
  }
  return contractAbi;
}

function compareInputs(inputsAbi, inputs){
  if (inputsAbi.length !== inputs.length) {
    return false;
  }
  for (let i = 0; i < inputsAbi.length; ++i) {
    if (inputs[i] !== inputsAbi[i].type) {
        return false;
    }
  }
  return true;
}
function showResult(result){
  console.log('打印结果',result);
  // this.result = this.result + result + "\n\n";
}

function _checkParameters(funct, params) {
  let name = funct.name;
  let inputs = funct.inputs;
  if (inputs.length !== params.length) {
      throw new Error(`wrong number of arguments for \`${name}\`, expected ${inputs.length} but got ${params.length}`);
  }
}
//获取随机id
function _genRandomID() {
  let uuid = uuidv4();
  uuid = uuid.replace(/-/g, '');
  if (!uuid.startsWith('0x')) {
      uuid = '0x' + uuid;
  }
  return uuid;
}
//组合abi参数
function _getTxData(func, params, encryptType) {
  // 计算函数标识 function(type,type,...)
  let inputsTypes = [];
  func.inputs.forEach((v, i) => { inputsTypes.push(v.type); });
  let str = func.name + "(" + inputsTypes.join(",") + ")";
  // 计算摘要（需要支持国密）
  let digest = fisco.hash(str, encryptType);
  // 拼接函数标识
  let txDataCode = '0x' + digest.slice(0, 8);
  // 拼接参数
  let inputs = func.inputs;
  let paramsCode = fisco.web3sync.encodeParams(inputs, params);
  if(paramsCode.slice(0, 2) === '0x'){
      paramsCode = paramsCode.slice(2);
  }
  txDataCode += paramsCode;
  return txDataCode;
}

export {reqweb3};
